#
# python-icontact2 - A python implementation of IContact API 2.0
# Copyright (c) 2010,2011 Alvin Delagon (http://www.alvinatorsplayground.blogspot.com/)
# All rights reserved.
#

import urllib
import urllib2
import httplib2
import json

ICONTACT_URL = "http://app.icontact.com/icp/signup.php"

class IContactConnectivityError(Exception):
    """
    Raised when iContact API calls fail
    """


class IContactInvalidResponse(Exception):
    """
    Raised when API returns a non-JSON response
    """

class IContactInvalidAccountError(Exception):
    """
    Raised when accountId is invalid
    """


class IContactNoFolderError(Exception):
    """
    Raised when there no folders for the account
    """


class IContactAccountIdNotSet(Exception):
    """
    Raised when accountId is not set yet
    """


class IContactFolderIdNotSet(Exception):
    """
    Raised when folderId is not set yet
    """


class IContactWebFormError(Exception):
    """
    Raised when there's an error on posting
    on a webform
    """


class IContactWebFormInvalidFields(Exception):
    """
    Raised when required webform fields are
    missing
    """


class IContactResponse:

    def __init__(self, headers, raw_content, json_content):
        self.headers = headers
        self.raw_content = raw_content
        self.json_content = json_content

    def prettyPrint(self):
        s  = "HEADERS: %s\n" % self.headers
        s += "PAYLOAD: %s\n" % self.json_content
        return s


class IContactPerson:

    def __init__(self, email, fields={}):
        self.email = email
        self.fields = fields

    def encode(self):
        fields = self.fields
        fields['email'] = self.email
        return json.dumps(fields)

    def getFields(self):
        fields = self.fields
        fields['email'] = self.email
        return fields


class IContactSession:

    def __init__(self, url, appId, username, password,
                 version="2.2", accountId=None, folderId=None):
        self.url = url
        self.appId = appId
        self.username = username
        self.password = password
        self.version = version
        self.accountId = accountId
        self.folderId = folderId

    def getHeaders(self):
        return {"Accept": "application/json",
                "Content-Type": "application/json",
                "Api-AppId": self.appId,
                "Api-Version": self.version,
                "Api-Username": self.username,
                "Api-Password": self.password}

    def checkIds(self, foldercheck=True):
        if not self.accountId:
            raise IContactAccountIdNotSet
        if not self.folderId and foldercheck:
            raise IContactFolderIdNotSet

    def setAccountId(self, accountId):
        self.accountId = accountId

    def setFolderId(self, folderId):
        self.folderId = folderId

    def callRemote(self, url, operation, options={}, body=None):
        if options:
            url += "?" + urllib.urlencode(options)

        h = httplib2.Http()
        try:
            if operation == "GET":
                headers, content = h.request(url, operation,
                                             headers=self.getHeaders())
            elif operation == "POST":
                headers, content = h.request(url, operation,
                                             json.dumps([body]),
                                             headers=self.getHeaders())
        except httplib2.ServerNotFoundError, e:
            raise IContactConnectivityError(e)
        try:
            json_content = json.loads(content)
        except ValueError, e:
            raise IContactInvalidResponse(e)
        return IContactResponse(headers, content, json_content)

    def getUrl(self):
        self.checkIds()
        return self.url + "a/" + self.accountId + "/c/" + self.folderId + "/"

    def getAccountIds(self):
        url = self.url + "a/"

        response = self.callRemote(url, "GET")
        headers = response.headers
        payload = response.json_content

        if not payload.get('accounts'):
            raise IContactInvalidAccountError

        accountIds = []
        for account in payload['accounts']:
            accountIds.append(account['accountId'])

        return accountIds

    def getFolderIds(self):
        self.checkIds(foldercheck=False)
        url = self.url + "a/" + self.accountId + "/c"

        response = self.callRemote(url, "GET")
        headers = response.headers
        payload = response.json_content

        if not payload.get('clientfolders'):
            raise IContactNoFolderError(e)

        folderIds = []
        for folder in payload['clientfolders']:
            folderIds.append(folder['clientFolderId'])

        return folderIds

    def getContact(self, contactId):
        url = self.getUrl() + "contacts/" + str(contactId)
        return self.callRemote(url, "GET")

    def getContacts(self, options={}):
        url = self.getUrl() + "contacts"
        return self.callRemote(url, "GET", options=options)

    def getMessages(self, options={}):
        url = self.getUrl() + "messages"
        return self.callRemote(url, "GET", options=options)

    def getMessage(self, msgid):
        url = self.getUrl() + "messages" + "/" + str(msgid)
        return self.callRemote(url, "GET")

    def getMessageStats(self, msgid):
        url = self.getUrl() + "messages" + "/" + str(msgid) + "/statistics"
        return self.callRemote(url, "GET")

    def addContact(self, contact):
        url = self.getUrl() + "contacts/"
        return self.callRemote(url, "POST", body=contact)
        #return self.callRemote(url, "POST", body=contact.getFields())



class IContactWebFormError(Exception):

    def __init__(self, value):
        self.value = value

    def __unicode__(self):
        return self.value


class IContactWebForm:

    def __init__(self, listid, specialid, formid, clientid,
                 params, reallistid='1', doubleopt='0'):
        self.url = ICONTACT_URL
        self.listid = listid
        self.specialid = "specialid:" + self.listid
        self.specialid_val = specialid
        self.formid = formid
        self.clientid = clientid
        self.params = params
        self.reallistid = reallistid
        self.doubleopt = doubleopt

    def post(self):
        data = {}
        data['listid'] = self.listid
        data[self.specialid] = self.specialid_val
        data['formid'] = self.formid
        data['clientid'] = self.clientid
        data['reallistid'] = self.reallistid
        data['doubleopt'] = self.doubleopt

        for key, val in self.params.items():
            data[key] = val

        try:
            result = urllib2.urlopen(self.url, urllib.urlencode(data))
        except Exception, e:
            raise IContactWebFormError(e)

        return result.read()

def getIContactWebForm(qdict, required={}, optional={}):
    params = {}
    # Required Parameters
    listid = qdict.get("icontact_listid")
    specialid = qdict.get("icontact_specialid")
    clientid = qdict.get("icontact_clientid")
    formid = qdict.get("icontact_formid")
    if not listid or not specialid or not clientid or not formid:
        raise IContactWebFormInvalidFields
    # User Required Fields
    for k, v in required.items():
        val = qdict.get(k)
        if not val:
            raise IContactWebFormInvalidFields
        params[v] = val
    # Optional User Fields
    for k, v in required.items():
        val = qdict.get(k, "")
        params[v] = val
    return IConactWebForm(listid, specialid, formid, clientid, params)
